#!/usr/bin/python
#***************************************************************************
# Copyright notice for this file:
#  Copyright (C) 2012 TheAxeMan
#  _______ _                               _    _
# |__   __| |     ___     /\          ___ | \  / |
#    | |  | |__  / _ \   /  \  __  _ / _ \|  \/  | __ _ _ __
#    | |  | '_ \|  __/  / /\ \ \ \/ |  __/| .  . |/ _` | '_ \
#    | |  | | | | (__/\/ ____ \ )  (| (__/| |\/| | (_| | | | |
#    |_|  |_| |_|\____/_/    \_._/\_.\____|_|  |_|\__,_|_| |_|
#
# Final Fantasy TAS scripts by TheAxeMan
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#***************************************************************************


from battlePlannerLib        import Combatant, BattleRandGenerator, CharType
from battlePlannerBattle     import Battle


class Garland(Combatant):
  damage = 15
  defense = 10
  num_hits = 1
  crit_threshold = 1
  hit_threshold = 142
  type = CharType.garland
  
class Lvl1Fighter(Combatant):
  damage = 10
  defense = 0
  num_hits = 1
  crit_threshold = 0
  hit_threshold = 166
  type = CharType.level1fighter

class Lvl1WhiteMageWithIronHammer(Combatant):
  damage = 11
  defense = 0
  num_hits = 1
  crit_threshold = 5
  hit_threshold = 161
  type = CharType.level1whitemage
  
class GarlandBattle(Battle):
  partyFightCost = 160
  dust_seeds = 135
  startingHeroHp = 35
  
  def __init__(self):
    '''Override to add the right combatants'''
    self.rand_engine = BattleRandGenerator()
    self.rand_engine.seed = 51
    
    self.enemy_list = [Combatant(0) for i in xrange(9)]
    self.enemy_list[2] = Garland(106)
    self.party_list = [Lvl1Fighter(35) for i in xrange(4)]
    #self.party_list = [Lvl1WhiteMageWithIronHammer(28) for i in xrange(4)]
    
    self.orderSeedCosts, self.orderFrameCosts = self.getOrderCosts()
    
  def getState(self):
    '''Override this to simplify, but make sure load matches'''
    return (self.rand_engine.seed, self.enemy_list[2].hp,
            tuple((p.hp for p in self.party_list)),
            )
      
  def loadState(self, state):
    '''Override this to simplify, but make sure save matches'''
    self.rand_engine.seed = state[0]
    self.enemy_list[2].hp = state[1]
    for i, hp in enumerate(state[2]):
      self.party_list[i].hp = hp
      
  def monstersPerished(self):
    return self.enemy_list[2].hp == 0
  
  seeds_used = [0 for i in xrange(9)]
  seeds_used[2] = 2
  frames_used = [0 for i in xrange(9)]
  frames_used[2] = 0
  def getOrderCosts(self):
    return GarlandBattle.seeds_used, GarlandBattle.frames_used
      
  def getPossibleOrders(self, roundNumber):
    possible_orders = set()
    for a in [-1,2] if self.party_list[0].hp > 0 else [-1]:
      for b in [-1,2] if self.party_list[1].hp > 0 else [-1]:
        for c in [-1,2] if self.party_list[2].hp > 0 else [-1]:
          for d in [-1,2] if self.party_list[3].hp > 0 else [-1]:
            possible_orders.add( (a,b,c,d) )
    possible_orders.remove( (-1,-1,-1,-1) )
    return possible_orders
    
  def getMaxWaitSeeds(self, roundNumber):
    if roundNumber == 1:
      return 110
    return 100
  
  lucky_list = []
  def getLuckyList(self):
    if len(GarlandBattle.lucky_list) == 0:
      #make a list of rands that are all right to end on
      luck_raise_list = self.rand_engine.luckRaiseSeeds()
      GarlandBattle.lucky_list = [False for i in xrange(256)]
  
      for i in luck_raise_list:
        #For first survivor, second, and so on
        GarlandBattle.lucky_list[i] = True
        #GarlandBattle.lucky_list[ (i+254) % 256 ] = True
        #GarlandBattle.lucky_list[ (i+251) % 256 ] = True
        #GarlandBattle.lucky_list[ (i+248) % 256 ] = True    
    
    return GarlandBattle.lucky_list
      
  def roundIsAcceptable(self, roundNumber, totalCost, roundDamage, enemyAttacks):
    #Aim for minimum number of rounds
    if roundNumber >= 4:
      return False
    #There is one more round to go, so cap cost below estimate
    if totalCost > 2400:
      return False
    if roundDamage < 20:
      return False
    if self.partyMembersLiving() < 3:
      return False
    return True

  def resultIsAcceptable(self, totalCost):
    if self.partyMembersLiving() != 3:
      return False
    if self.rand_engine.seed in [158,206]:  #Monsters strike first
      return False
    if self.rand_engine.seed in [112, 99, 87, 148, 188]:  #Try for better levelup
      return False
      
    #return True
    return self.gotLuck()
  
  def gotLuck(self):
    endSeed = self.rand_engine.seed
    luckyList = self.getLuckyList()
    #Want second or third char after fight to get luck.
    firstCharLuck = luckyList[ (endSeed+2) % 256 ]
    secondCharLuck = luckyList[ (endSeed+5) % 256 ]
    thirdCharLuck = luckyList[ (endSeed+8) % 256 ]
    if self.party_list[0].hp <= 0:
      return firstCharLuck or secondCharLuck
    else:
      return secondCharLuck or thirdCharLuck

def setupGarlandBattle():
  BattleRandGenerator.populateBattleOrderLookupTable()
  BattleRandGenerator.populateTargetLookupTable()
  
  garland = Garland(106)
  fighter = Lvl1Fighter(35)
  #whiteMage = Lvl1WhiteMageWithIronHammer(28)
  
  Lvl1Fighter.populateDamageLookupTable(garland)
  #Lvl1WhiteMageWithIronHammer.populateDamageLookupTable(garland)
  Garland.populateDamageLookupTable(fighter)
  
  return GarlandBattle()

def garland_demo():
  """Demonstrate battle flow for garland fight"""
  garland_fight = setupGarlandBattle()
  
  order_list = []
  if 0:
    order_list.append([-1,2,-1,2])
    order_list.append([-1,-1,-1,2])
    order_list.append([2,2,2,-1])
    order_list.append([2,-1,2,2])
    wait_list = [1, 45, 66, 31]
  if 0:
    order_list.append([-1,2,-1,2])
    order_list.append([2,2,-1,2])
    order_list.append([-1,2,2,2])
    wait_list = [1, 27, 43]
  if 1:
    order_list.append([2,2,2,2])
    order_list.append([-1,2,2,2])
    order_list.append([-1,-1,-1,2])
    wait_list = [104, 162, 66]

  for i in xrange(len(wait_list)):
    garland_fight.rand_engine.useUpRands(wait_list[i])

    #r = garland_fight.rand_engine.copy()
    #print r.getBattleOrder()
    #r = garland_fight.rand_engine.copy()
    #for j in xrange(34):
    #  print r.getRandInRange(0, 12),
    #print ""

    garland_fight.simulateRound(order_list[i], print_attack=True)
    print "seed is",garland_fight.rand_engine.seed
    print ""
    
def optimizeGarland():
  garland_fight = setupGarlandBattle()
  garland_fight.optimize()
    
if __name__ == "__main__":
  #garland_demo()  
  optimizeGarland()  
  